package com.shinektv.heartbeat;

import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Build;
import android.util.Log;

import androidx.annotation.BinderThread;
import androidx.annotation.RequiresApi;

import com.shinektv.utils.ByteUtils;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * author: liqi
 * created on 2023/3/30
 * localSocket 客户端公共属性封装
 * 封装的 service和client是一对一的
 * 且程序启动 new一个实例只能被初始一次
 * 断开重连 由封装保证
 */
public class LocalClientSocketCommon {

    private LocalSocket mSocket = null;
    private String mSocketName = this.getClass().getSimpleName();
    private InputStream mInputStream = null;
    private OutputStream mOutputStream = null;
    private Thread mThread = null;
    private volatile boolean isRun = true;

    private volatile boolean isConnect = false;
    //计数第一次打印 后每10次打印
    private volatile int countPrint = 0;

    private SocketListener mListener;

    private ProtocolCommonTransfer mTransfer = new ProtocolCommonTransfer();

    private volatile boolean mStopMessage = false;


    private ProtocolCommonPacketListener mProtocolCommonPacketListener = new ProtocolCommonPacketListener() {
        @Override
        public void onReceivePacket(ProtocolCommonPacket data) {
            if (data.getBodyLength() > 0) {
                String key = new String(data.getBodyContent(), 0, data.getBodyLength());
                if (mListener != null) {
                    mListener.onReceiveMessage(key);
                }

                Log.d(mSocketName, "ServerActivity mSocketOutStream==" + key);
                if ("stop".equals(key)) {
                    mStopMessage = true;
                }
            } else {
                if (data.getDataType() == ProtocolCommonPacket.SEND_DATA_TYPE_PING) {

                } else if (data.getDataType() == ProtocolCommonPacket.SEND_DATA_TYPE_PONG) {

                }
            }
        }
    };

    public void init(String socketName, SocketListener listener) {
        if (mThread != null) return;
        isRun = true;
        mSocketName = socketName;
        mListener = listener;
        mTransfer.setParsePacketListener(mProtocolCommonPacketListener);
        startTime();
        mThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (isRun) {
                    createServerSocket(mSocketName);

                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        mThread.setName(socketName);
        mThread.start();
    }

    public void uninit() {
        isRun = false;
        mStopMessage = true;
        stopTime();
        closeSocketResource();
        if (mThread != null) {
            mThread.interrupt();
            mThread = null;
        }
    }

    private void createServerSocket(String socketName) {
        if (mSocket == null) {
            try {
                //创建LocalSocket，模拟客户端
                mSocket = new LocalSocket();
                LocalSocketAddress address = new LocalSocketAddress(socketName,
                        LocalSocketAddress.Namespace.ABSTRACT);
                //连接LocalSocketServer
                mSocket.connect(address);
                countPrint = 0;
                acceptMsg();

            } catch (IOException ex) {
                mSocket = null;
                if (countPrint % 10 == 0) {
                    ex.printStackTrace();
                }
                countPrint++;
            }
        }
    }


    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    private void closeSocketResource() {
        closeSlient(mInputStream);
        closeSlient(mOutputStream);
        closeSlient(mSocket);
        try {
            if (mSocket != null) {
                mSocket.close();
                mSocket = null;
            }
        } catch (IOException ex) {
            Log.e(mSocketName, "Failed closing ServerSocket" + ex.fillInStackTrace());
        }
    }

    private void closeSlient(Closeable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
                closeable = null;
            }
        } catch (IOException ex) {
            Log.e(mSocketName, "Failed closing : " + closeable);
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    private void acceptMsg() {
        try {
            mOutputStream = mSocket.getOutputStream();
            isConnect = true;
            if (mListener != null) {
                mListener.onConnect();
            }
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        while (isRun) {
            try {
                byte[] buffer = new byte[1024];
                mInputStream = mSocket.getInputStream();
                int count = mInputStream.read(buffer);
                if (count != -1) {
                    if (mTransfer != null) {
                        mTransfer.writeData(Arrays.copyOfRange(buffer, 0, count));
                    }
                    if (mStopMessage) {
                        mStopMessage = false;
                        closeSocketResource();
                        break;
                    }
                } else {
                    Log.e(mSocketName, "连接断开========");
                    isConnect = false;
                    if (mListener != null) {
                        mListener.onDisconnect();
                    }
                    closeSocketResource();
                    break;
                }

            } catch (Exception e) {
                Log.e(mSocketName, "exception==" + e.fillInStackTrace().getMessage());
                e.printStackTrace();
                break;
            }
        }
    }

    private ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

    public void sendMsg(final String msg) {
        if (!isConnect) {
            Log.e(mSocketName, "未连接localsocket服务端. msg = " + msg);
            return;
        }
        singleThreadExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    if (mOutputStream != null) {
                        sendDataImp(ProtocolCommonPacket.SEND_DATA_TYPE_BINARY, msg);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 实际发送的数据
     * @param type
     * @param msg
     * @return
     */
    private boolean sendDataImp(int type, String msg) {
        if (msg == null || mOutputStream == null)
            return false;

        try {
            byte[] bytes = msg.getBytes("utf-8");
            ByteBuffer sendBuf = ByteBuffer.allocate(bytes.length + 8);//8字节信息头
            sendBuf.put((byte) 0xff);
            sendBuf.put((byte) 0xff);
            sendBuf.put((byte) 0xff);
            sendBuf.put((byte) type);
            sendBuf.put(ByteUtils.intToBytes(bytes.length));
            sendBuf.put(bytes);
            try {
                mOutputStream.write(ByteUtils.subByte(sendBuf.array(), sendBuf.arrayOffset(), bytes.length + 8));
                sendBuf.clear();
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(mSocketName, "send error " + msg);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return true;
    }

    public boolean isConnect() {
        return isConnect;
    }

    @BinderThread
    public interface SocketListener {
        public void onConnect();

        public void onReceiveMessage(String msg);

        public void onDisconnect();


    }


    private Timer timer = null;//计时器
    private TimerTask task = null;

    //启动
    private void startTime() {
        if (timer == null) {
            timer = new Timer();
        }
        task = new TimerTask() {
            @Override
            public void run() {
                try {
                    sendMsg("ping");

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        };
        //1000ms执行一次
        timer.schedule(task, 500, 15000);
    }

    private void stopTime() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }
}